
#ifndef __LOGER__
#define __LOGER__

#include <iostream>
#include <atomic>
#include <thread>
#include <mutex>
#include <cstdarg>
#include <atomic>
#include <condition_variable>
#include <unordered_map>

#include "format.hpp"
#include "sink.hpp"
#include "buffer.hpp"

// 日志器模块，用于生成日志器

namespace log_p
{
    // 功能：用于整合日志格式化模块，日志落地模块
    // 由于日志器模式下有两种模式(同步&异步模式)，所以日志器真正落地时，使用派生类完成定义
    class Logger
    {
    public:
        using ptr = std::shared_ptr<Logger>;

        Logger(const std::string &logger_name,
               log_level::values limit_level,
               log_p::Formatter::ptr formatter_fty,
               std::vector<log_p::LogSink::ptr> &sink_queue)
            : _logger_name(logger_name), _formatter_fty(formatter_fty), _limit_level(limit_level),
              _sink_pool(sink_queue)
        {
        }

        // 基础的debug, info, error, fail, warn
        void Debug(const std::string &file_name, size_t line_, const std::string &fmt, ...)
        {
            if (log_level::values::DEBUG < _limit_level)
                return;
            // 1.从不定参中获取字符串
            char custom_log[1024] = {0};
            va_list v_li;        // 本质是 char*
            va_start(v_li, fmt); // 设置成format这样将打印全部自定义内容
            vsnprintf(custom_log, sizeof custom_log, fmt.c_str(), v_li);
            va_end(v_li);
            // 2.产生message对象
            // 3.formater产生最终日志字符串
            // 获取最终日志信息
            std::string log_result = Serialize(log_p::log_level::values::DEBUG, custom_log, file_name, line_);

            log(log_result.c_str(), log_result.size());
        }

        void Info(std::string _file_name, size_t _line_, const std::string &fmt, ...)
        {
            // 写在日常文件夹或者不写
            // std::cout << 1 << std::endl;
            if (log_level::values::INFO < _limit_level)
                return;
            char custom_log[1024] = {0};
            va_list v_li;        // 本质是 char*
            va_start(v_li, fmt); // 设置成format这样将打印全部自定义内容S
            vsnprintf(custom_log, sizeof custom_log, fmt.c_str(), v_li);
            va_end(v_li);
            std::string log_result = Serialize(log_p::log_level::values::INFO, custom_log, _file_name, _line_);
            // 写在文件 bylog中
            log(log_result.c_str(), log_result.size());
        }

        void Fail(std::string _file_name, size_t _line_, const std::string &fmt, ...)
        {
            // 写在错误文件夹，并保存到云端
            if (log_level::values::FAIL < _limit_level)
                return;
            char custom_log[1024] = {0};
            va_list v_li;        // 本质是 char*
            va_start(v_li, fmt); // 设置成format这样将打印全部自定义内容S
            vsnprintf(custom_log, sizeof custom_log, fmt.c_str(), v_li);
            va_end(v_li);

            std::string log_result = "\033[31m";
            log_result.append(Serialize(log_p::log_level::values::FAIL, custom_log, _file_name, _line_) + "\033[0m");
            // 设置颜色便于终端观察
            // 写在文件 bylog中
            log(log_result.c_str(), log_result.size());
        }

        void Warn(std::string _file_name, size_t _line_, const std::string &fmt, ...)
        {
            // 写在本地
            if (log_level::values::WARN < _limit_level)
                return;
            char custom_log[1024] = {0};
            va_list v_li;        // 本质是 char*
            va_start(v_li, fmt); // 设置成format这样将打印全部自定义内容S
            vsnprintf(custom_log, sizeof custom_log, fmt.c_str(), v_li);
            va_end(v_li);

            std::string log_result = "\033[33m";
            log_result.append(Serialize(log_p::log_level::values::WARN, custom_log, _file_name, _line_) + "\033[0m");
            // 设置颜色便于终端观察
            log(log_result.c_str(), log_result.size());
        }

    protected:
        // 调用日志器
        virtual bool log(const char *str, size_t length) = 0;

        std::mutex _mutex;                           // 日志落地操作，防止日志交叉
        std::vector<log_p::LogSink::ptr> _sink_pool; // 日志落地方向数组

    private:
        std::string Serialize(log_p::log_level::values tmp, const std::string &custom_log,
                              const std::string &file_, size_t line_)
        {
            log_p::message mess(tmp, _logger_name, custom_log, file_, line_);
            auto it = _formatter_fty->GetResult(mess);
            return it;
        }

        std::string _logger_name;             // 日志器唯一名称
        log_p::Formatter::ptr _formatter_fty; // 日志格式化工厂

        std::atomic<log_level::values> _limit_level; // 防止多线程对其进行修改
    };

    class SynLogger : public Logger
    {
    public:
        SynLogger(const std::string &logger_name,
                  log_level::values limit_level,
                  log_p::Formatter::ptr formatter_fty,
                  std::vector<log_p::LogSink::ptr> &sink_queue)
            : Logger(logger_name, limit_level, formatter_fty, sink_queue)
        {
        }

    private:
        bool log(const char *str, size_t length)
        {
            // 在调用sink_queue的log中会对文件进行修改，加锁的目的就在这里
            // 同步日志器：多线程由线程自己落地日志，因此存在线程安全;
            // 而异步模式下，多线程都将信息传递到缓冲区，由单独一个线程负责落地.因此其不需要加锁
            std::unique_lock<std::mutex> it(_mutex);
            for (auto &tmp : _sink_pool)
            {
                tmp->log(str, length);
            }
        }
    };

    class AsynLogger : public Logger
    {
    public:
        typedef std::function<void(log_p::buffer &)> thread_callback;

        AsynLogger(const std::string &logger_name,
                   log_level::values limit_level,
                   log_p::Formatter::ptr formatter_fty,
                   std::vector<log_p::LogSink::ptr> &sink_queue)
            : Logger(logger_name, limit_level, formatter_fty, sink_queue), _pro_buffer(), _write_buffer()
        {
            _stop = false;
            _buffer_thread = std::thread(std::bind(&AsynLogger::Thread_Running, this));
            _thr_callback = std::bind(&AsynLogger::CallBack, this, std::placeholders::_1);
        }

        void SetAsynloggerThread_CallBack(thread_callback callback)
        {
            _thr_callback = callback;
        }

        ~AsynLogger()
        {
            while (1)
            {
                if (_pro_buffer.IsOK() && _write_buffer.IsOK())
                {
                    closeThread();
                    _pro_con_cond.notify_all(); // 唤醒全部工作线程，并等待线程退出
                    _buffer_thread.join();
                    break;
                }
            }
        }

    private:
        bool log(const char *str, size_t length)
        {
            // 向缓冲区中载入日志信息，线程检测缓冲区变化
            if (_stop == true)
                return false;
            {
                std::unique_lock<std::mutex> it(pro_mutex);
                // 检测输入缓冲区条件，false则阻塞
                _pro_con.wait(it, [&]() mutable
                              { return _pro_buffer.GetAbleSize() >= length; });
                _pro_buffer.push({str, length});
            }
            _pro_con_cond.notify_one(); // 唤醒消费者线程
        }

        void CallBack(log_p::buffer &PopBuffer)
        {
            for (auto &tmp : _sink_pool)
            {
                tmp->log(PopBuffer.GetReadPointer(), PopBuffer.GetWriteCur());
            }
        }

        void Thread_Running()
        {
            while (!_stop)
            {
                {
                    std::unique_lock<std::mutex> it(pro_mutex);
                    // 如果不是达成交换缓冲区条件，则阻塞
                    _pro_con_cond.wait(it,
                                       [&]
                                       { return (_pro_buffer.GetWriteCur() != 0 && _write_buffer.GetWriteCur() == 0) || _stop; });
                    _pro_buffer.swap(_write_buffer);
                }
                _pro_con.notify_all(); // 通知所有生产者

                // 同步日志器：多线程由线程自己落地日志，因此存在线程安全;
                // 而异步模式下，多线程都将信息传递到缓冲区，由单独一个线程负责落地.因此其不需要加锁
                // 除非：你想要让异步日志器与同步日志器同时跑
                _thr_callback(_write_buffer);
                _write_buffer.ReSet();
            }
        }

        void closeThread() { _stop = true; }

    protected:
        thread_callback _thr_callback;

    private:
        log_p::buffer _pro_buffer;   // 生产者缓冲区
        log_p::buffer _write_buffer; // 消费者缓冲区

        std::mutex pro_mutex;       // 生产者关系锁,消费者不设置锁，一般分配一个线程即可
        std::thread _buffer_thread; // 用于服务日志输出

        std::atomic<bool> _stop;               // true表示停止异步日志线程;false 继续
        std::condition_variable _pro_con;      // 生产者之间：关注输入缓冲区
        std::condition_variable _pro_con_cond; // 生产与消费者之间：关注两者无法交换数据，如输入缓冲区未空
    };

}
#endif
